/*
 *  Arrow
 *
 *  Created by David Giovannini on 1/5/07.
 *  Copyright 2007 Software by Jove. All rights reserved.
 *
 */
 
#ifndef SBJ_Arrow_Channel_h
#define SBJ_Arrow_Channel_h

#include "SBJ/Arrow/IPublisher.h"
#include "SBJ/Arrow/Queue.h"
#include "SBJ/Arrow/MessageHolder.h"
#include "SBJ/Arrow/IDispatcher.h"
#include "SBJ/Arrow/Subscription.h"
#include "SBJ/Arrow/PublicationMgr.h"
#include "SBJ/Interfaces/IExceptionObserver.h"

#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/logic/tribool.hpp>

#include <vector>
#include <iosfwd>

namespace SBJ
{

namespace Arrow
{

class Channel : protected IDispatcher, private boost::noncopyable
{
public:
	enum
	{
		dispatch_none = -1,
		dispatch_immediately = 0
	};

	typedef IDispatcher::Stats Stats;
	typedef boost::shared_ptr< Channel > Ptr;
	
	Channel( const IExceptionObserver::Ptr& excObserver = IExceptionObserver::Ptr() );
	
	Channel(	int numMsgHolderThreads,	// dispatch_none
				int numDispatchingThreads,	// dispatch_immediately
				size_t priorityCount = 1,
				EnqueuePolicy* enqueuePolicy = 0,
				DequeuePolicy* dequeuePolicy = 0,
				const IExceptionObserver::Ptr& excObserver = IExceptionObserver::Ptr() );
	
	virtual ~Channel( void );

	inline bool isDispatching( void ) const;
	
	bool start( void );
	
	bool stop( bool join = true );

	inline Queue::Stats getQueueStats( void ) const;

	virtual IDispatcher::Stats getDispatchStats( void ) const { return m_dispatcher->getDispatchStats(); }
	
	inline MessageHolder::Stats getMessageHolderStats( void ) const;

	void printStats( std::ostream& out ) const;

	template < typename MsgType >
	Publication& publish( const std::string& pubKey = std::string() );
	
	template < typename MsgType, typename Allocator >
	Publication& publishWithAlloc( const Allocator& allocator, const std::string& pubKey = std::string() );

	void addFeed( const IPublisher::Ptr& feed, boost::tribool start = boost::logic::indeterminate );

	template < typename MsgType, typename Subscriber >
	Subscription makeTypedSubscription( Subscriber* subscriber, const std::string& topicKey, const std::string& filter = std::string() );
	
	template < typename MsgType, typename Subscriber >
	Subscription makeSubscription( Subscriber* subscriber, const std::string& pubKey, const std::string& topicKey, const std::string& filter = std::string() );

	Subscription makeSubscription( void* subscriber, const std::string& pubKey, const std::string& topicKey, const std::string& filter = std::string() );
		
	virtual void onMessage( Message& msg );

	void onMessage( Message& msg, float inSeconds ); // MTODO

private:
	typedef std::vector< IPublisher::Ptr > Feeds;
	
	void init(	int numMsgHolderThreads,
				int numDispatchingThreads,
				size_t priorityCount,
				EnqueuePolicy* enqueuePolicy,
				DequeuePolicy* dequeuePolicy,
				const IExceptionObserver::Ptr& excObserver );
	
	template < typename MsgType >
	Topic getTopic( const std::string& topicKey = std::string() ) const;
	
	Topic getTopic( const std::string& pubKey, const std::string& topicKey = std::string() ) const;

	virtual bool subscribe( const Subscription& subscription );
	
	virtual bool unsubscribe( const Subscription& subscription );

	PublicationMgr m_pubMgr;
	volatile bool m_started;
	const bool m_useDispatcher;
	Feeds m_feeds;
	IRouter::Ptr m_entry;
	MessageHolder::Ptr m_msgHolder;
	Queue::Ptr m_queue;
	IDispatcher::Ptr m_dispatcher;
};

}

}

#include "Channel.inl"

#endif